#include "stdafx.h"
#include "XListViewImpl.h"


#include <Util.h>
#include <IXColorDraw.h>
#include <IXScrollBar.h>

//////////////////////////////////////////////////////////////////////////

#define DEFAULT_UPDATE_VALUE    0
#define MAX_UPDATE_VALUE        35535

template <class I>
CXListViewImpl<I>::CXListViewImpl()
{
    m_pNormalBkg = NULL;
    m_pHoverBkg = NULL;
    m_pSelectedBkg = NULL;

    m_pHeader = NULL;
    m_pContainerFrame = NULL;

    m_pListDelegate = NULL;
    m_bAutoRemoveDelegate = FALSE;

    m_nCurSel = NULL_ITEM;
    m_nCurHover = NULL_ITEM;

    m_nNormalHeight = 50;
    m_nSelectedHeight = -1;

    m_nUpdateValue = DEFAULT_UPDATE_VALUE;
}

template <class I>
CXListViewImpl<I>::~CXListViewImpl()
{
}


//////////////////////////////////////////////////////////////////////////
// IXListView
template <class I>
int CXListViewImpl<I>::InsertColumn(IN int nIndex, IN LPCTSTR szText)
{
    if(m_pHeader == NULL)
        return -1;

    int nResult = m_pHeader->InsertItem(nIndex, szText);
    if(nResult >= 0)
    {
        m_pHeader->put_Size(GetLineFrameWidth(TRUE), m_pHeader->GetHeight());
    }
    return nResult;
}

template <class I>
int CXListViewImpl<I>::InsertColumn(IN int nIndex, IN LPCTSTR szText, IN int nWidth)
{
    if(m_pHeader == NULL)
        return -1;

    int nResult = m_pHeader->InsertItem(nIndex, szText, DT_SINGLELINE | DT_CENTER | DT_VCENTER | DT_END_ELLIPSIS, nWidth);
    if(nResult >= 0)
    {
        m_pHeader->put_Size(GetLineFrameWidth(TRUE), m_pHeader->GetHeight());
    }
    return nResult;
}

template <class I>
void CXListViewImpl<I>::SetColumnFlags(IN int nIndex, IN DWORD dwFlags)
{
    XASSERT_ERROR(m_pHeader != NULL, _T("Error in IXListView::SetColumnFlags"));
    if(m_pHeader != NULL)
    {
        m_pHeader->SetItemFlags(nIndex, dwFlags);
    }
}

template <class I>
DWORD CXListViewImpl<I>::GetColumnFlags(IN int nIndex)
{
    XASSERT_ERROR(m_pHeader != NULL, _T("Error in IXListView::GetColumnFlags"));
    if(m_pHeader != NULL)
    {
        return m_pHeader->GetItemFlags(nIndex);
    }
    return 0;
}

template <class I>
int CXListViewImpl<I>::GetSortIndex()
{
    XASSERT_ERROR(m_pHeader != NULL, _T("Error in IXListView::GetColumnFlags"));
    if(m_pHeader != NULL)
    {
        return m_pHeader->GetSortIndex();
    }
    return 0;
}

template <class I>
BOOL CXListViewImpl<I>::GetSortAsc()
{
    XASSERT_ERROR(m_pHeader != NULL, _T("Error in IXListView::GetColumnFlags"));
    if(m_pHeader != NULL)
    {
        return m_pHeader->GetSortAsc();
    }
    return 0;
}

template <class I>
int CXListViewImpl<I>::GetCurSel()
{
    return m_nCurSel;
}

template <class I>
void CXListViewImpl<I>::SetCurSel(int nIndex)
{
    if(m_nCurSel == nIndex)
        return;

    if(m_nCurSel != NULL_ITEM)
    {
        IXFrame* pFrame = m_vctLineData[m_nCurSel].vctFrame[0];
        if(m_nCurSel == m_nCurHover)
        {
            pFrame->put_Background(m_pHoverBkg);
        }
        else
        {
            pFrame->put_Background(m_pNormalBkg);
            pFrame->put_Size(pFrame->GetWidth(), m_nNormalHeight);
        }
    }
    m_nCurSel = nIndex;
    if(m_nCurSel != NULL_ITEM)
    {
        IXFrame* pFrame = m_vctLineData[m_nCurSel].vctFrame[0];
        pFrame->put_Background(m_pSelectedBkg);
        pFrame->put_Size(pFrame->GetWidth(), GetSelectedHeight());
    }
}

template <class I>
int CXListViewImpl<I>::InsertItem(IN size_t nIndex)
{
    LineData stubData;
    stubData.nUpdateValue = DEFAULT_UPDATE_VALUE;
    nIndex = (nIndex <= m_vctLineData.size()) ? nIndex : m_vctLineData.size();
    LineDataVector::iterator IteData = m_vctLineData.insert(m_vctLineData.begin() + nIndex, stubData);

    if(m_nCurSel != NULL_ITEM && nIndex <= m_nCurSel)
        ++ m_nCurSel;
    if(m_nCurHover != NULL_ITEM && nIndex <= m_nCurHover)
        ++ m_nCurHover;

    IXFrame* pLineFrame = NULL;
    LineData& data = m_vctLineData[nIndex];
    FrameVector& vctFrame = data.vctFrame;

    // Line Frame
    if(FAILED(Util::CreateObject(&pLineFrame)))
        return FALSE;

    RECT rcClient;

    int nLineWidth = GetLineFrameWidth(FALSE);
    RECT rcStub = {0, 0, nLineWidth, m_nNormalHeight};
    pLineFrame->Create(m_pContainerFrame, &rcStub, NULL);
    pLineFrame->get_ClientRect(&rcClient);
    m_pContainerFrame->put_ChildZOrder(pLineFrame, nIndex);
    // pLineFrame->put_MinSize(nLineWidth, -1);

    pLineFrame->put_Background(m_pNormalBkg);
    pLineFrame->ModifyControlFlags(ESkin_CanHover, TRUE);
    pLineFrame->ModifyControlFlags(ESkin_Interactive, TRUE);
    RegisterEvent(pLineFrame, FrameEvent_Clicked, &CXListViewImpl<I>::OnLineFrameClicked);
    RegisterEvent(pLineFrame, FrameEvent_RightClicked, &CXListViewImpl<I>::OnLineFrameRightClicked);
    RegisterEvent(pLineFrame, ControlEvent_FlagsChanged, &CXListViewImpl<I>::OnLineFrameHover);

    vctFrame.push_back(pLineFrame);

    UpdateLineFrameWidth();

    return nIndex;
}

template <class I>
void CXListViewImpl<I>::RemoveItem(IN size_t nIndex)
{
    if(nIndex >= m_vctLineData.size())
        return;

    LineData& data = m_vctLineData[nIndex];
    FrameVector& vctFrame = data.vctFrame;
    IXFrame* pLineFrame = vctFrame[0];
    m_vctLineData.erase(m_vctLineData.begin() + nIndex);

    if(m_nCurSel == nIndex)
        m_nCurSel = NULL_ITEM;
    if(m_nCurHover == nIndex)
        m_nCurHover = NULL_ITEM;

    m_pContainerFrame->DelChild(pLineFrame);

    UpdateLineFrameWidth();
}

template <class I>
void CXListViewImpl<I>::RemoveAllItems()
{
    m_pContainerFrame->SetLayoutable(FALSE);
    LineDataVector::iterator iteLine = m_vctLineData.begin();
    for(; iteLine != m_vctLineData.end(); ++ iteLine)
    {
        LineData& data = *iteLine;
        FrameVector& vctFrame = data.vctFrame;
        IXFrame* pLineFrame = vctFrame[0];

        m_pContainerFrame->DelChild(pLineFrame);
    }
    m_vctLineData.clear();
    m_pContainerFrame->SetLayoutable(TRUE);

    m_nCurSel = NULL_ITEM;
    m_nCurHover = NULL_ITEM;
}

template <class I>
void CXListViewImpl<I>::SetItemCount(IN size_t nCount)
{
    size_t nLineCount = m_vctLineData.size();
    if(nCount > nLineCount)
    {
        m_pContainerFrame->SetLayoutable(FALSE);
        for(size_t i=nLineCount; i<nCount; ++ i)
        {
            InsertItem(i);
        }
        m_pContainerFrame->SetLayoutable(TRUE);
        UpdateLineFrameWidth();
        m_pHeader->put_Size(GetLineFrameWidth(TRUE), m_pHeader->GetHeight());
    }
    else if(nCount < nLineCount)
    {
        m_pContainerFrame->SetLayoutable(FALSE);
        for(size_t i=nLineCount; i + 1 > nCount + 1; -- i)
        {
            RemoveItem(i - 1);
        }
        m_pContainerFrame->SetLayoutable(TRUE);
        UpdateLineFrameWidth();
    }
    UpdateAllItems();
}

template <class I>
size_t CXListViewImpl<I>::GetItemCount()
{
    return m_vctLineData.size();
}

template <class I>
void CXListViewImpl<I>::SetListDelegate(IN IXListDelegate* pListDelegate, IN BOOL bAutoRemoveDelegate)
{
    m_pListDelegate = pListDelegate;
    m_bAutoRemoveDelegate = bAutoRemoveDelegate;
    if(m_pListDelegate != NULL)
    {
        m_pListDelegate->SetContainer(this);
    }
}

template <class I>
IXFrame* CXListViewImpl<I>::GetItemFrame(IN size_t nItem, IN size_t nSubItem)
{
    if(nItem >= m_vctLineData.size())
        return NULL;

    LineData& data = m_vctLineData[nItem];
    FrameVector& vctFrame = data.vctFrame;

    if(data.vctFrame.size() <= 1)
        if(!CreateItem(nItem))
            return NULL;

    if(nSubItem + 1>= vctFrame.size())
        return NULL;

    return vctFrame[nSubItem + 1];
}

template <class I>
void CXListViewImpl<I>::SetNormalBackgnd(IN IXDraw* pNormalBkg)
{
    XASSERT_ERROR(m_vctLineData.size() == 0, _T("Backgnd need to be set before Insertion of first Item"));
    XSAFE_RELEASE(m_pNormalBkg);
    m_pNormalBkg = pNormalBkg;
    if(m_pNormalBkg != NULL)
        m_pNormalBkg->AddRef();
}

template <class I>
void CXListViewImpl<I>::SetHoverBackgnd(IN IXDraw* pHoverBkg)
{
    XASSERT_ERROR(m_vctLineData.size() == 0, _T("Backgnd need to be set before Insertion of first Item"));
    XSAFE_RELEASE(m_pHoverBkg);
    m_pHoverBkg = pHoverBkg;
    if(m_pHoverBkg != NULL)
        m_pHoverBkg->AddRef();
}

template <class I>
void CXListViewImpl<I>::SetSelectedBackgnd(IN IXDraw* pSelectedBkg)
{
    XASSERT_ERROR(m_vctLineData.size() == 0, _T("Backgnd need to be set before Insertion of first Item"));
    XSAFE_RELEASE(m_pSelectedBkg);
    m_pSelectedBkg = pSelectedBkg;
    if(m_pSelectedBkg != NULL)
        m_pSelectedBkg->AddRef();
}

template <class I>
void CXListViewImpl<I>::SetNormalHeight(IN size_t nHeight)
{
    XASSERT_ERROR(m_vctLineData.size() == 0, _T("Height need to be set before Insertion of first Item"));
    m_nNormalHeight = nHeight;
    if(m_pContainerFrame != NULL)
    {
        m_pContainerFrame->SetScrollLine(m_nNormalHeight);
    }
}

template <class I>
void CXListViewImpl<I>::SetSelectedHeight(IN size_t nHeight)
{
    XASSERT_ERROR(m_vctLineData.size() == 0, _T("Height need to be set before Insertion of first Item"));
    m_nSelectedHeight = nHeight;
}

template <class I>
int CXListViewImpl<I>::GetFirstVisibleIndex()
{
    return m_pContainerFrame->GetFirstVisibleIndex();
}

template <class I>
int CXListViewImpl<I>::GetVisibleCount()
{
    return m_pContainerFrame->GetVisibleCount();
}

template <class I>
void CXListViewImpl<I>::UpdateAllItems()
{
    if( (++ m_nUpdateValue) >= MAX_UPDATE_VALUE)
    {
        m_nUpdateValue = DEFAULT_UPDATE_VALUE;
    }
    UpdateVisibleData();
}

template <class I>
void CXListViewImpl<I>::UpdateItem(IN size_t nItem)
{
    if(nItem <= m_vctLineData.size() && nItem < 0x80000000)
    {
        LineData& data = m_vctLineData[nItem];
        UpdateItemData(nItem);
        ++ data.nUpdateValue;
    }
}

//////////////////////////////////////////////////////////////////////////
// IXControl
template <class I>
void CXListViewImpl<I>::OnCreate()
{
    superclass::OnCreate();
    // Normal
    IXColorDraw* pNormalClrDraw = NULL;
    if(FAILED(Util::CreateObject(&pNormalClrDraw)))
        return;
    pNormalClrDraw->put_Color(RGB(255, 255, 255));
    m_pNormalBkg = pNormalClrDraw;

    // Hover
    IXColorDraw* pHoverClrDraw = NULL;
    if(FAILED(Util::CreateObject(&pHoverClrDraw)))
        return;
    pHoverClrDraw->put_Color(RGB(255, 0, 255));
    m_pHoverBkg = pHoverClrDraw;

    // Selected
    IXColorDraw* pSelectedClrDraw = NULL;
    if(FAILED(Util::CreateObject(&pSelectedClrDraw)))
        return;
    pSelectedClrDraw->put_Color(RGB(255, 255, 0));
    m_pSelectedBkg = pSelectedClrDraw;

    // Header
    if(FAILED(Util::CreateObject(&m_pHeader)))
        return;
    POINT PtStub = {0};
    m_pHeader->Create(this, &PtStub, _XSKIN_HEADER_ID);
    m_pHeader->put_MinSize(GetWidth(), -1);
    m_pHeader->SetAutoSize(FALSE);

    RegisterEvent(m_pHeader, HeaderEvent_WidthChanged, &CXListViewImpl<I>::OnHeaderColWidthChanged);
    RegisterEvent(m_pHeader, HeaderEvent_Clicked, &CXListViewImpl<I>::OnHeaderClicked);

    // Layout
    if(FAILED(Util::CreateObject(&m_pContainerFrame)))
        return;

    m_pContainerFrame->SetScrollLine(m_nNormalHeight);

    int nHeaderHeight = m_pHeader->GetHeight();
    RECT rcFrame = {0, nHeaderHeight, GetWidth(), GetHeight()};
    m_pContainerFrame->Create(this, &rcFrame, NULL);
    m_pContainerFrame->SetLayoutType(Layout_Vert);
    m_pContainerFrame->EnableScrollBar(TRUE);
    m_pContainerFrame->ModifyControlFlags(ESkin_Interactive, TRUE);
    m_pContainerFrame->SetFrameLayoutType(FLT_HFill | FLT_VFill);
    m_pContainerFrame->put_Margin(0, m_pHeader->GetHeight(), 0, 0);
    m_pContainerFrame->ModifyControlFlags(ESkin_Interactive, TRUE);

    m_pHeader->put_Size(GetLineFrameWidth(TRUE), m_pHeader->GetHeight());

    RegisterEvent(m_pContainerFrame, FrameEvent_SBPosChanged, &CXListViewImpl<I>::OnContainerFrameSBChanged);
}

template <class I>
void CXListViewImpl<I>::OnSizeChanged()
{
    superclass::OnSizeChanged();

    int nWidth = GetWidth();
    if(m_pHeader != NULL)
    {
        m_pHeader->put_MinSize(nWidth, -1);
    }

    int nLineFrameWidth = GetLineFrameWidth(FALSE);
    int nLineCount = m_vctLineData.size();

    SIZE Autosize = {0};
    m_pContainerFrame->GetAutoSize(&Autosize);

    BOOL bNeedVertSB = FALSE;
    {
        IXControl* pVertSB = NULL;
        m_pContainerFrame->GetItemById(_XSKIN_VERT_SCROLL_ID, &pVertSB);
        bNeedVertSB = (pVertSB != NULL && pVertSB->IsVisible());
    }

    if(bNeedVertSB)
    {
        IXControl* pVertSB = NULL;
        m_pContainerFrame->GetItemById(_XSKIN_VERT_SCROLL_ID, &pVertSB);
        nWidth -= (pVertSB == NULL) ? 0 : pVertSB->GetWidth();
    }

    m_pContainerFrame->SetLayoutable(FALSE);
    for(int nLine=0; nLine < nLineCount; ++ nLine)
    {
        LineData& data = m_vctLineData[nLine];

        IXFrame*& pFrame = data.vctFrame[0];
        // pFrame->put_MinSize(nWidth, -1);
        pFrame->put_Size(nLineFrameWidth, pFrame->GetHeight());
    }
    m_pContainerFrame->SetLayoutable(TRUE);
    m_pHeader->put_Size(GetLineFrameWidth(TRUE), m_pHeader->GetHeight());
}

template <class I>
void CXListViewImpl<I>::PreDraw(XCANVAS xCanvas, const RECT& rcDraw)
{
    superclass::PreDraw(xCanvas, rcDraw);
    UpdateVisibleData();
}

//////////////////////////////////////////////////////////////////////////
// IXBaseObj
template <class I>
void CXListViewImpl<I>::OnFinalRelease()
{
    superclass::OnFinalRelease();

    XSAFE_RELEASE(m_pNormalBkg);
    XSAFE_RELEASE(m_pHoverBkg);
    XSAFE_RELEASE(m_pSelectedBkg);

    if(m_pListDelegate != NULL && m_bAutoRemoveDelegate)
    {
        delete m_pListDelegate;
        m_pListDelegate = NULL;
    }
}

//////////////////////////////////////////////////////////////////////////
// Event
template <class I>
void CXListViewImpl<I>::OnLineFrameClicked(IXControl* pSrcControl, LPARAM lParam)
{
    int nCurSel = m_pContainerFrame->get_ChildZOrder(pSrcControl);
    SetCurSel(nCurSel);
}

template <class I>
void CXListViewImpl<I>::OnLineFrameRightClicked(IXControl* pSrcControl, LPARAM lParam)
{
    int nCurSel = m_pContainerFrame->get_ChildZOrder(pSrcControl);
    SetCurSel(nCurSel);

    POINT* pt = (POINT*)lParam;
    ShowContextMenu(pt);
}

template <class I>
void CXListViewImpl<I>::OnLineFrameHover(IXControl* pSrcControl, LPARAM lParam)
{
    ControlEventInfo* pInfo = (ControlEventInfo*)lParam;
    if(pInfo->flag == ESkin_Hover)
    {
        if(pInfo->bAdd)
        {
            int nCurSel = m_pContainerFrame->get_ChildZOrder(pSrcControl);
            SetCurHover(nCurSel);
        }
        else
        {
            SetCurHover(NULL_ITEM);
        }
    }
}

template <class I>
void CXListViewImpl<I>::OnHeaderColWidthChanged(IXControl* pSrcControl, LPARAM lParam)
{
    IXFrame* pFrame = NULL;
    int nLineCount = m_vctLineData.size();
    int nColCount = m_pHeader->GetItemCount();

    if(m_pContainerFrame != NULL)
        m_pContainerFrame->SetLayoutable(FALSE);

    BOOL bNeedUpdateWidth = FALSE;
    int nLineFrameWidth = GetLineFrameWidth(FALSE);
    if(nLineCount > 0 && m_vctLineData[0].vctFrame.size() > 0)
    {
        LineData& data = m_vctLineData[0];
        int nWidth = data.vctFrame[0]->GetWidth();
        bNeedUpdateWidth = (nWidth != nLineFrameWidth);
    }

    int nLeft = 0;
    int nColWidth = 0;
    for(int nCol=0; nCol<nColCount; ++ nCol)
    {
        nColWidth = m_pHeader->GetItemWidth(nCol);
        for(int nLine=0; nLine < nLineCount; ++ nLine)
        {
            LineData& data = m_vctLineData[nLine];

            if(nCol == 0 && bNeedUpdateWidth)
            {
                IXFrame*& pFrame = data.vctFrame[0];
                pFrame->put_Size(nLineFrameWidth, pFrame->GetHeight());
            }

            if(nCol + 1 < (int)data.vctFrame.size())
            {
                pFrame = data.vctFrame[nCol + 1];
                pFrame->put_Pos(nLeft, pFrame->GetTop());
                pFrame->put_Size(nColWidth, pFrame->GetHeight());
            }
        }
        nLeft += nColWidth;
    }

    if(m_pContainerFrame != NULL)
        m_pContainerFrame->SetLayoutable(TRUE);

    m_pHeader->put_Size(GetLineFrameWidth(TRUE), m_pHeader->GetHeight());

    Invalidate();
}

template <class I>
void CXListViewImpl<I>::OnHeaderClicked(IXControl* pSrcControl, LPARAM lParam)
{
    stHeaderClickedEventInfo* pInfo = (stHeaderClickedEventInfo*)lParam;

    if(m_pListDelegate != NULL)
    {
        m_pListDelegate->Sort(pInfo->nItemIndex, pInfo->bAsc);
    }
}

template <class I>
void CXListViewImpl<I>::OnContainerFrameSBChanged(IXControl* pSrcControl, LPARAM lParam)
{
    stFmEvtSBPosChangedParam* pParam = (stFmEvtSBPosChangedParam*)lParam;
    if(pParam->bHorz)
    {
        if(m_pHeader->GetTotalWidth() + GetVertScrollBarWidth() <= GetWidth())
            m_pHeader->put_Pos(0, m_pHeader->GetTop());
        else
            m_pHeader->put_Pos(-pParam->nCurPos, m_pHeader->GetTop());
    }
}

//////////////////////////////////////////////////////////////////////////
// XListViewImpl

template <class I>
void CXListViewImpl<I>::SetCurHover(int nHover)
{
    if(m_nCurHover == nHover)
        return;

    int nHeight = m_nNormalHeight;
    if(m_nCurHover != NULL_ITEM)
    {
        IXFrame* pFrame = m_vctLineData[m_nCurHover].vctFrame[0];
        if(m_nCurHover == m_nCurSel)
        {
            pFrame->put_Background(m_pSelectedBkg);
            nHeight = GetSelectedHeight();
        }
        else
        {
            pFrame->put_Background(m_pNormalBkg);
            nHeight = m_nNormalHeight;
        }
        pFrame->put_Size(pFrame->GetWidth(), nHeight);
    }
    m_nCurHover = nHover;
    if(m_nCurHover != NULL_ITEM)
    {
        IXFrame* pFrame = m_vctLineData[m_nCurHover].vctFrame[0];
        if(m_nCurHover != m_nCurSel)
        {
            pFrame->put_Background(m_pHoverBkg);
        }
    }
}

template <class I>
BOOL CXListViewImpl<I>::CreateItem(int nIndex)
{
    IXFrame* pSubFrame = NULL;
    IXFrame* pLineFrame = NULL;
    LineData& data = m_vctLineData[nIndex];
    FrameVector& vctFrame = data.vctFrame;

    // Line Frame
    pLineFrame = vctFrame[0];

    RECT rcStub = {0, 0, GetWidth(), m_nNormalHeight};
    ::SetRect(&rcStub, 0, 0, 0, m_nNormalHeight);

    int nColumnCount = m_pHeader->GetItemCount();
    for(int i=0; i<nColumnCount; ++ i)
    {
        if(FAILED(Util::CreateObject(&pSubFrame)))
            break;

        rcStub.right = rcStub.left + m_pHeader->GetItemWidth(i);
        pSubFrame->Create(pLineFrame, &rcStub, NULL);
        rcStub.left = rcStub.right;
        pSubFrame->SetFrameLayoutType(FLT_VFill);
        vctFrame.push_back(pSubFrame);
    }
    if(m_pListDelegate != NULL)
        m_pListDelegate->OnCreateItem(nIndex, pLineFrame);
    return TRUE;
}

template <class I>
void CXListViewImpl<I>::UpdateVisibleData()
{
    if(m_pListDelegate != NULL)
    {
        int nFirst = GetFirstVisibleIndex();
        int nCount = GetVisibleCount();
        if(nFirst >= 0)
        {
            nCount += nFirst;
            for(int i=nFirst; i < nCount; ++ i)
            {
                UpdateItemData(i);
            }
        }
    }
}

template <class I>
void CXListViewImpl<I>::UpdateItemData(int nIndex)
{
    LineData& data = m_vctLineData[nIndex];
    if(data.vctFrame.size() <= 1)
        if(!CreateItem(nIndex))
            return;
    if(data.vctFrame.size() > 1 && m_nUpdateValue != data.nUpdateValue)
    {
        m_pListDelegate->OnResetItem(nIndex);
        data.nUpdateValue = m_nUpdateValue;
    }
}

template <class I>
void CXListViewImpl<I>::UpdateLineFrameWidth()
{
    if(!m_pContainerFrame->GetLayoutable())
        return;

    BOOL bNeedUpdateWidth = FALSE;
    int nLineCount = m_vctLineData.size();
    int nLineFrameWidth = GetLineFrameWidth(FALSE);
    if(nLineCount > 0 && m_vctLineData[0].vctFrame.size() > 0)
    {
        LineData& data = m_vctLineData[0];
        int nWidth = data.vctFrame[0]->GetWidth();
        bNeedUpdateWidth = (nWidth != nLineFrameWidth);
    }

    if(bNeedUpdateWidth)
    {
        m_pContainerFrame->SetLayoutable(FALSE);
        for(int nLine=0; nLine < nLineCount; ++ nLine)
        {
            LineData& data = m_vctLineData[nLine];

            IXFrame*& pFrame = data.vctFrame[0];
            pFrame->put_Size(nLineFrameWidth, pFrame->GetHeight());
        }
        m_pContainerFrame->SetLayoutable(TRUE);
    }
}

template <class I>
int CXListViewImpl<I>::GetVertScrollBarWidth()
{
    IXControl* pVertSB = NULL;
    m_pContainerFrame->GetItemById(_XSKIN_VERT_SCROLL_ID, &pVertSB);
    int nWidth = (pVertSB == NULL) ? 0 : pVertSB->GetWidth();
    return nWidth;
}

template <class I>
size_t  CXListViewImpl<I>::GetSelectedHeight()
{
    if(m_nSelectedHeight == -1)
        return m_nNormalHeight;
    return m_nSelectedHeight;
}

template <class I>
int CXListViewImpl<I>::GetLineFrameWidth(BOOL bHeader)
{
    XASSERT_ERROR(m_pHeader != NULL, _T(""));
    int nTotalWidth = m_pHeader->GetTotalWidth();
    int nListWidth = GetWidth();

    int nVertSBWidth = 0;
    {
        IXControl* pVertSB = NULL;
        m_pContainerFrame->GetItemById(_XSKIN_VERT_SCROLL_ID, &pVertSB);
        BOOL bNeedVertSB = (pVertSB != NULL && pVertSB->IsVisible());
        if(bNeedVertSB)
            nVertSBWidth = pVertSB->GetWidth();
    }

    int nWidth = 0;

    if(nTotalWidth <= nListWidth - nVertSBWidth)
    {
        if(bHeader)
            nWidth = nListWidth;
        else
            nWidth = nListWidth - nVertSBWidth;
    }
    else
    {
        if(bHeader)
            nWidth = nTotalWidth + nVertSBWidth;
        else
            nWidth = nTotalWidth;
    }

    return nWidth;
}
